/*! 
 * Header file for the rigid body class.
 *
 * \author Devis Rossini
 * \date 17 March 2012
 */ 
#ifndef PHXRIGIDBODY_H_INCLUDE_GUARD
#define PHXRIGIDBODY_H_INCLUDE_GUARD

#include "PhxCore.h"
#include "PhxRigidBodyDescriptor.h"

namespace phx
{
	/*!
     * A rigid body is the basic simulation object of the engine.
     */
	class RigidBody
	{
	protected:

		#pragma region Characteristic data

		/*!
         * \name Characteristic data
         *
		 * Characteristics are properties of the rigid body
         * independent of its current kinematic situation. This
         * includes mass, moment of inertia and damping
         * properties. Two identical rigid bodys will have the same
         * values for their characteristics.
         */
        /*@{*/

		/*!
         * Holds the inverse of the mass of the rigid body.         
		 */
        real mInverseMass;

		/*!
         * Holds the inverse of the body's inertia tensor, specified
		 * in the body's local space.
         */
        Matrix3 mInverseInertiaTensor;

		/*!
         * Holds the amount of damping applied to linear
         * motion.
         */
        real mLinearDamping;

		/*!
         * Holds the amount of damping applied to angular
         * motion.
         */
        real mAngularDamping;
		
		/*@}*/

		#pragma endregion

		#pragma region Flags

		RigidBodyTypes mBodyType;

		#pragma endregion

		#pragma region Kinematic state

		/*!
         * \name Kinematic state
         *
		 * Kinematic description of the rigid body.
         */
        /*@{*/

        /*!
         * Holds the linear position of the rigid body in
         * world space.
         */
        Vector3 mPosition;

		/*!
         * Holds the angular orientation of the rigid body in
         * world space.
         */
        Quaternion mOrientation;

		/*!
         * Holds the linear velocity of the rigid body in
         * world space.
         */
        Vector3 mVelocity;

		/*!
         * Holds the angular velocity of the rigid body in 
		 * world space.
         */
        Vector3 mAngularVelocity;

		/*@}*/

		#pragma endregion

		#pragma region Derived data

		/*!
         * \name Derived data
         *
         * These data members hold information that is derived from
         * the other data in the class.
         */
        /*@{*/

        /*!
         * Holds the inverse inertia tensor of the body in world
         * space.
         */
        Matrix3 mInverseInertiaTensorWorld;

		/*!
         * Holds a weighted mean that represent the motion of the 
		 * body. It can be used to put a body to sleap.
         */
		real mMotion;

		/*!
         * A body can be put to sleep to avoid it being updated
         * by the integration functions or affected by collisions
         * with the world.
         */
        bool mIsAwake;

		/*!
         * Some bodies should be always awake.
         */
        bool mCanSleep;

		/*!
         * Holds a transform matrix for converting body space into
         * world space (and vice versa).
         */
        Matrix4 mTransformMatrix;

		/*@}*/

		#pragma endregion	

		#pragma region Force and torque accumulators

		/*!
         * \name Force and Torque Accumulators
         *
         * These data members store the current force, torque and
         * acceleration of the rigid body. Forces can be added to the
         * rigid body in any order, and the class decomposes them into
         * their constituents, accumulating them for the next
         * simulation step. At the simulation step, the accelerations
         * are calculated and stored to be applied to the rigid body.
         */
        /*@{*/

		/*!
         * Holds the accumulated force to be applied at the next
         * integration step.
         */
        Vector3 mForceAccumulator;

		/*!
         * Holds the accumulated torque to be applied at the next
         * integration step.
         */
        Vector3 mTorqueAccumulator;

		/*!
         * Holds the acceleration of the rigid body. This value
         * can be used to set acceleration due to gravity (its primary
         * use), or any other constant acceleration.
         */
        Vector3 mAcceleration;

		/*!
         * Holds the linear acceleration of the rigid body, for the
         * previous frame.
         */
        Vector3 mLastFrameAcceleration;

        /*@}*/

		#pragma endregion

	public:

		#pragma region Constructors

		RigidBody();

		RigidBody(const RigidBodyDescriptor& desc);

		void SetupRigidBody(const RigidBodyDescriptor& desc);

		#pragma endregion

		#pragma region Flags accessor

		/*!
		 * Return the type of the body (static, dynamic, etc.).
		 */
		RigidBodyTypes GetBodyType() const;

		/*!
		 * Check if the body is static.
		 */
		bool IsStatic() const;
		/*!
		 * Check if the body is dynamic.
		 */
		bool IsDynamic() const;

		/*!
		 * Set the body type to static. The mass will become zero.
		 */
		void SetStatic();
		/*!
		 * Set the body type to dynamic (the body mass must be specified).
		 */
		void SetDynamic(real mass);

		#pragma endregion	

		#pragma region Integration functions

		/*!
         * \name Integration functions
         *
         * These functions are used to simulate the rigid body's
         * motion.
		 *
		 * Currently the only integration function provided is the
         * first order Newton Euler method.
         */
        /*@{*/

		/*!
         * Calculates internal data from state data. This should be called
         * after the body's state is altered directly (it is called
         * automatically during integration).
         */
        void CalculateDerivedData();

        /*!
         * Integrates the rigid body forward in time by the given amount.
		 * This function uses a Newton-Euler integration method, which is a
		 * linear approximation to the correct integral. For this reason it
		 * may be inaccurate in some cases.
		 */
        void Integrate(real duration);

        /*@}*/

		#pragma endregion

		#pragma region Accessor functions for characteristic data

		/*!
         * \name Accessor functions for body's state
         *
         * These functions provide access to the rigid body's
         * characteristics or state.
         */
        /*@{*/
		
		#pragma region Mass

		/*!
         * Sets the mass of the rigid body.
		 *
		 * \note The mass may not be zero.
         */
        void SetMass(const real mass);

		/*!
         * Gets the mass of the rigid body.
         */
        real GetMass() const;

		/*!
         * Sets the inverse mass of the rigid body.
         */
        void SetInverseMass(const real inverseMass);

		/*!
         * Gets the inverse mass of the rigid body.
         */
        real GetInverseMass() const;

        /*!
         * Returns true if the mass of the body is not-infinite.
         */
        bool HasFiniteMass() const;

		#pragma endregion

		#pragma region Inertia tensor

        /*!
         * Sets the intertia tensor for the rigid body.
         */
        void SetInertiaTensor(const Matrix3& inertiaTensor);

        /*!
         * Copies the current inertia tensor of the rigid body into
         * the given matrix.
         */
        void GetInertiaTensor(Matrix3* inertiaTensor) const;

		/*!
         * Gets a copy of the current inertia tensor of the rigid body.
         */
        Matrix3 GetInertiaTensor() const;

        /*!
         * Copies the current inertia tensor of the rigid body expressed 
		 * in world space into the given matrix.
         */
        void GetInertiaTensorWorld(Matrix3* inertiaTensor) const;

        /*!
         * Gets a copy of the current inertia tensor of the rigid body
		 * expressed in world space. 
         */
        Matrix3 GetInertiaTensorWorld() const;

		/*!
         * Sets the inverse intertia tensor for the rigid body.
         */
        void SetInverseInertiaTensor(const Matrix3& inverseInertiaTensor);

        /*!
         * Copies the current inverse inertia tensor of the rigid body
         * into the given matrix.
         */
        void GetInverseInertiaTensor(Matrix3* inverseInertiaTensor) const;

		/*!
         * Gets a copy of the current inverse inertia tensor of the
         * rigid body.
         */
        Matrix3 GetInverseInertiaTensor() const;

        /*!
         * Copies the current inverse inertia tensor of the rigid body
         * expressed in world space into the given matrix.
         */
        void GetInverseInertiaTensorWorld(Matrix3 *inverseInertiaTensor) const;

        /*!
         * Gets a copy of the current inverse inertia tensor of the
         * rigid body expressed in world space.
         */
        Matrix3 GetInverseInertiaTensorWorld() const;

		#pragma endregion

		#pragma region Damping

		/*!
         * Sets both linear and angular damping.
         */
        void SetDamping(const real linearDamping, const real angularDamping);

        /*!
         * Sets the linear damping for the rigid body.
         */
        void SetLinearDamping(const real linearDamping);

        /*!
         * Gets the current linear damping value.
         */
        real GetLinearDamping() const;

        /*!
         * Sets the angular damping for the rigid body.
         */
        void SetAngularDamping(const real angularDamping);

        /*!
         * Gets the current angular damping value.
         */
        real GetAngularDamping() const;

		#pragma endregion

		#pragma region Position

		/*!
         * Sets the position of the rigid body.
         */
        void SetPosition(const Vector3& position);

        /*!
         * Sets the position of the rigid body.
         */
        void SetPosition(const real x, const real y, const real z);

        /*!
         * Fills the given vector with the position of the rigid body.
         */
        void GetPosition(Vector3* position) const;

        /*!
         * Gets the position of the rigid body.
         */
        Vector3 GetPosition() const;

		#pragma endregion
	
		#pragma region Orientation

		/*!
         * Sets the orientation of the rigid body.
         */
        void SetOrientation(const Quaternion& orientation);

        /*!
         * Sets the orientation of the rigid body.
         */
        void SetOrientation(const real r, const real i, const real j, const real k);

        /*!
         * Fills the given quaternion with the current value of the
         * rigid body's orientation.
         */
        void GetOrientation(Quaternion *orientation) const;

        /*!
         * Gets the orientation of the rigid body.
         */
        Quaternion GetOrientation() const;

        /*!
         * Fills the given matrix with a transformation representing
         * the rigid body's orientation.
         */
        void GetOrientation(Matrix3* matrix) const;

        /*!
         * Fills the given matrix data structure with a transformation
         * representing the rigid body's orientation.
         */
        void GetOrientation(real matrix[9]) const;

		#pragma endregion

		#pragma region Transform matrix 

        /*!
         * Fills the given matrix with a transformation representing
         * the rigid body's position and orientation.
         */
        void GetTransform(Matrix4* transform) const;

        /*!
         * Fills the given matrix data structure with a
         * transformation representing the rigid body's position and
         * orientation.
         */
        void GetTransform(real matrix[16]) const;

		/*!
         * Gets a transformation representing the rigid body's
         * position and orientation.
         */
        Matrix4 GetTransform() const;

		/*!
         * Converts the given point from world space into the body's
         * local space.
         */
        Vector3 GetPointInLocalSpace(const Vector3& point) const;

		/*!
         * Converts the given point from body's local space into the
         * world space.
         */
        Vector3 GetPointInWorldSpace(const Vector3& point) const;

        /*!
         * Converts the given direction from world space into the
         * body's local space.
         */
        Vector3 GetDirectionInLocalSpace(const Vector3& direction) const;

        /*!
         * Converts the given direction from body's local space into the
         * world space.
         */
        Vector3 GetDirectionInWorldSpace(const Vector3& direction) const;

		#pragma endregion

		#pragma region Velocity

		/*!
         * Sets the velocity of the rigid body.
		 *
		 * \note The velocity is given in world space.
         */
        void SetVelocity(const Vector3& velocity);

		/*!
         * Sets the velocity of the rigid body. 
		 *
		 * \note The velocity is given in world space.
         */
        void SetVelocity(const real x, const real y, const real z);

        /*!
         * Fills the given vector with the velocity of the rigid body.
         */
        void GetVelocity(Vector3* velocity) const;

        /*!
         * Gets the velocity of the rigid body.
         */
        Vector3 GetVelocity() const;

        /*!
         * Applies the given change in velocity.
         */
        void AddVelocity(const Vector3& deltaVelocity);

		#pragma endregion

		#pragma region Angular velocity

		/*!
         * Sets the angular velocity of the rigid body.
         *
         * \note The angular velocity is given in world space.
         */
        void SetAngularVelocity(const Vector3& angularVelocity);

        /*!
		 * Sets the angular velocity of the rigid body.
		 *
		 * \note The angular velocity is given in world space.
         */
        void SetAngularVelocity(const real x, const real y, const real z);

        /*!
         * Fills the given vector with the angular velocity of the rigid body.
         */
        void GetAngularVelocity(Vector3* angularVelocity) const;

        /*!
         * Gets the angular velocity of the rigid body.
         */
        Vector3 GetAngularVelocity() const;

        /*!
         * Applies the given change in angular velocity.
         */
        void AddAngularVelocity(const Vector3& deltaAngularVelocity);

		#pragma endregion

		#pragma region Awake state

        /*!
         * Returns true if the body is awake and responding to
         * integration.
         */
        bool GetAwake() const
        {
            return mIsAwake;
        }

        /*!
         * Sets the awake state of the body.
         */
        void SetAwake(const bool awake = true);

        /*!
         * Returns true if the body is allowed to go to sleep.
         */
        bool GetCanSleep() const
        {
            return mCanSleep;
        }

        /*!
         * Sets whether the body is ever allowed to go to sleep.
         */
        void SetCanSleep(const bool canSleep = true);

        /*@}*/

		#pragma endregion

		#pragma endregion

		#pragma region Accessor functions for dynamic quantities

        /*!
         * \name Accessor functions for dynamic quantities
         *
         * These functions provide access to the acceleration
         * properties of the body.
         */
        /*@{*/

		/*!
         * Fills the given vector with the current accumulated value
         * for linear acceleration (in world space).
         */
        void GetLastFrameAcceleration(Vector3* linearAcceleration) const;

        /*!
         * Gets the current accumulated value for linear
         * acceleration (in world space).
         */
        Vector3 GetLastFrameAcceleration() const;

		/*@}*/

		#pragma endregion

		#pragma region Force, torque and acceleration

        /*!
         * \name Force, torque and acceleration set-up functions
         *
         * These functions set up forces and torques to apply to the
         * rigid body.
         */
        /*@{*/

        /*!
         * Clears the forces and torques in the accumulators. This will
         * be called automatically after each intergration step.
         */
        void ClearAccumulators();

        /*!
         * Adds the given force to centre of mass of the rigid body.
         *
		 * \note The force is expressed in world space.
         */
        void AddForce(const Vector3& force);

        /*!
         * Adds the given force to the given point on the rigid body.
         *
		 * Because the force is not applied at the centre of mass, 
		 * it may be split into both a force and torque.
		 *
		 * \note Both the force and the application point are given 
		 * in world space. 
         */
        void AddForceAtPoint(const Vector3& force, const Vector3& point);

        /*!
         * Adds the given force to the given point on the rigid body.
         * 
		 * Because the force is not applied at the centre of mass, 
		 * it may be split into both a force and torque.
		 *
		 * \note The direction of the force is given in world space,
		 * but the application point is given in body space.
         */
        void AddForceAtBodyPoint(const Vector3& force, const Vector3& point);

        /*!
         * Adds the given torque to the rigid body.
		 *
         * \note The force is expressed in world space.
		 */
        void AddTorque(const Vector3& torque);

        /*!
         * Sets the constant acceleration of the rigid body.
         */
        void SetAcceleration(const Vector3& acceleration);

        /*!
         * Sets the constant acceleration of the rigid body.
         */
        void SetAcceleration(const real x, const real y, const real z);

        /*!
         * Fills the given vector with the acceleration of the rigid body.
         */
        void GetAcceleration(Vector3* acceleration) const;

        /*!
         * Gets the acceleration of the rigid body.
         */
        Vector3 GetAcceleration() const;

        /*@}*/

		#pragma endregion
	};
}

#endif // PHXRIGIDBODY_H_INCLUDE_GUARD